From b8e42407516900ab7d2e28ef3899684c1e072ab5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Timm=20B=C3=A4der?= Date: Fri, 18 Sep 2020 14:42:36 +0200 Subject: [PATCH] gl renderer: Add radial gradient shader --- gsk/gl/gskglrenderer.c | 63 ++++++++++++++++- gsk/gl/gskglrenderops.c | 29 ++++++++ gsk/gl/gskglrenderopsprivate.h | 28 +++++++- gsk/gl/opbuffer.c | 1 + gsk/gl/opbuffer.h | 41 +++++++----- gsk/gskrendernodeimpl.c | 2 - gsk/meson.build | 1 + gsk/resources/glsl/radial_gradient.glsl | 89 +++++++++++++++++++++++++ 8 files changed, 235 insertions(+), 19 deletions(-) create mode 100644 gsk/resources/glsl/radial_gradient.glsl diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index 00fb5aea5e..96aecbd70b 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -1192,6 +1192,32 @@ render_linear_gradient_node (GskGLRenderer *self, load_vertex_data (ops_draw (builder, NULL), node, builder); } +static inline void +render_radial_gradient_node (GskGLRenderer *self, + GskRenderNode *node, + RenderOpBuilder *builder) +{ + const float scale = ops_get_scale (builder); + const int n_color_stops = MIN (8, gsk_radial_gradient_node_get_n_color_stops (node)); + const GskColorStop *stops = gsk_radial_gradient_node_peek_color_stops (node, NULL); + const graphene_point_t *center = gsk_radial_gradient_node_peek_center (node); + const float start = gsk_radial_gradient_node_get_start (node); + const float end = gsk_radial_gradient_node_get_end (node); + const float hradius = gsk_radial_gradient_node_get_hradius (node); + const float vradius = gsk_radial_gradient_node_get_vradius (node); + + ops_set_program (builder, &self->programs->radial_gradient_program); + ops_set_radial_gradient (builder, + n_color_stops, + stops, + builder->dx + center->x, + builder->dy + center->y, + start, end, + hradius * scale, vradius * scale); + + load_vertex_data (ops_draw (builder, NULL), node, builder); +} + static inline gboolean rounded_inner_rect_contains_rect (const GskRoundedRect *rounded, const graphene_rect_t *rect) @@ -2761,6 +2787,25 @@ apply_linear_gradient_op (const Program *program, glUniform2f (program->linear_gradient.end_point_location, op->end_point[0], op->end_point[1]); } +static inline void +apply_radial_gradient_op (const Program *program, + const OpRadialGradient *op) +{ + OP_PRINT (" -> Radial gradient"); + if (op->n_color_stops.send) + glUniform1i (program->radial_gradient.num_color_stops_location, op->n_color_stops.value); + + if (op->color_stops.send) + glUniform1fv (program->radial_gradient.color_stops_location, + op->n_color_stops.value * 5, + (float *)op->color_stops.value); + + glUniform1f (program->radial_gradient.start_location, op->start); + glUniform1f (program->radial_gradient.end_location, op->end); + glUniform2f (program->radial_gradient.radius_location, op->radius[0], op->radius[1]); + glUniform2f (program->radial_gradient.center_location, op->center[0], op->center[1]); +} + static inline void apply_border_op (const Program *program, const OpBorder *op) @@ -2903,6 +2948,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, { "/org/gtk/libgsk/glsl/cross_fade.glsl", "cross fade" }, { "/org/gtk/libgsk/glsl/inset_shadow.glsl", "inset shadow" }, { "/org/gtk/libgsk/glsl/linear_gradient.glsl", "linear gradient" }, + { "/org/gtk/libgsk/glsl/radial_gradient.glsl", "radial gradient" }, { "/org/gtk/libgsk/glsl/outset_shadow.glsl", "outset shadow" }, { "/org/gtk/libgsk/glsl/repeat.glsl", "repeat" }, { "/org/gtk/libgsk/glsl/unblurred_outset_shadow.glsl", "unblurred_outset shadow" }, @@ -2984,6 +3030,14 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, start_point); INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, end_point); + /* radial gradient */ + INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, color_stops); + INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, num_color_stops); + INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, center); + INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, start); + INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, end); + INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, radius); + /* blur */ INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_radius); INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_size); @@ -3332,6 +3386,10 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self, render_linear_gradient_node (self, node, builder); break; + case GSK_RADIAL_GRADIENT_NODE: + render_radial_gradient_node (self, node, builder); + break; + case GSK_CLIP_NODE: render_clip_node (self, node, builder); break; @@ -3388,7 +3446,6 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self, break; case GSK_REPEATING_LINEAR_GRADIENT_NODE: - case GSK_RADIAL_GRADIENT_NODE: case GSK_REPEATING_RADIAL_GRADIENT_NODE: case GSK_CAIRO_NODE: default: @@ -3684,6 +3741,10 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self) apply_linear_gradient_op (program, ptr); break; + case OP_CHANGE_RADIAL_GRADIENT: + apply_radial_gradient_op (program, ptr); + break; + case OP_CHANGE_BLUR: apply_blur_op (program, ptr); break; diff --git a/gsk/gl/gskglrenderops.c b/gsk/gl/gskglrenderops.c index 1d48112804..7713f294eb 100644 --- a/gsk/gl/gskglrenderops.c +++ b/gsk/gl/gskglrenderops.c @@ -960,3 +960,32 @@ ops_set_linear_gradient (RenderOpBuilder *self, op->end_point[0] = end_x; op->end_point[1] = end_y; } + +void +ops_set_radial_gradient (RenderOpBuilder *self, + guint n_color_stops, + const GskColorStop *color_stops, + float center_x, + float center_y, + float start, + float end, + float hradius, + float vradius) +{ + const guint real_n_color_stops = MIN (MAX_GRADIENT_STOPS, n_color_stops); + OpRadialGradient *op; + + /* TODO: State tracking? */ + + op = ops_begin (self, OP_CHANGE_RADIAL_GRADIENT); + op->n_color_stops.value = real_n_color_stops; + op->n_color_stops.send = true; + op->color_stops.value = color_stops; + op->color_stops.send = true; + op->center[0] = center_x; + op->center[1] = center_y; + op->radius[0] = hradius; + op->radius[1] = vradius; + op->start = start; + op->end = end; +} diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h index ebb39e355a..a6c6d0f232 100644 --- a/gsk/gl/gskglrenderopsprivate.h +++ b/gsk/gl/gskglrenderopsprivate.h @@ -13,7 +13,7 @@ #include "opbuffer.h" #define GL_N_VERTICES 6 -#define GL_N_PROGRAMS 13 +#define GL_N_PROGRAMS 14 #define MAX_GRADIENT_STOPS 8 typedef struct @@ -62,6 +62,14 @@ struct _Program int start_point_location; int end_point_location; } linear_gradient; + struct { + int num_color_stops_location; + int color_stops_location; + int center_location; + int start_location; + int end_location; + int radius_location; + } radial_gradient; struct { int blur_radius_location; int blur_size_location; @@ -143,6 +151,14 @@ typedef struct float start_point[2]; float end_point[2]; } linear_gradient; + struct { + int n_color_stops; + GskColorStop color_stops[MAX_GRADIENT_STOPS]; + float center[2]; + float start; + float end; + float radius[2]; /* h/v */ + } radial_gradient; }; } ProgramState; @@ -161,6 +177,7 @@ typedef struct { Program cross_fade_program; Program inset_shadow_program; Program linear_gradient_program; + Program radial_gradient_program; Program outset_shadow_program; Program repeat_program; Program unblurred_outset_shadow_program; @@ -278,6 +295,15 @@ void ops_set_linear_gradient (RenderOpBuilder *self, float start_y, float end_x, float end_y); +void ops_set_radial_gradient (RenderOpBuilder *self, + guint n_color_stops, + const GskColorStop *color_stops, + float center_x, + float center_y, + float start, + float end, + float hradius, + float vradius); GskQuadVertex * ops_draw (RenderOpBuilder *builder, const GskQuadVertex vertex_data[GL_N_VERTICES]); diff --git a/gsk/gl/opbuffer.c b/gsk/gl/opbuffer.c index 496308c5ae..f212e96eec 100644 --- a/gsk/gl/opbuffer.c +++ b/gsk/gl/opbuffer.c @@ -15,6 +15,7 @@ static guint op_sizes[OP_LAST] = { sizeof (OpTexture), sizeof (OpRepeat), sizeof (OpLinearGradient), + sizeof (OpRadialGradient), sizeof (OpColorMatrix), sizeof (OpBlur), sizeof (OpShadow), diff --git a/gsk/gl/opbuffer.h b/gsk/gl/opbuffer.h index 9604f4b852..dcdedc6c50 100644 --- a/gsk/gl/opbuffer.h +++ b/gsk/gl/opbuffer.h @@ -23,21 +23,22 @@ typedef enum OP_CHANGE_SOURCE_TEXTURE = 9, OP_CHANGE_REPEAT = 10, OP_CHANGE_LINEAR_GRADIENT = 11, - OP_CHANGE_COLOR_MATRIX = 12, - OP_CHANGE_BLUR = 13, - OP_CHANGE_INSET_SHADOW = 14, - OP_CHANGE_OUTSET_SHADOW = 15, - OP_CHANGE_BORDER = 16, - OP_CHANGE_BORDER_COLOR = 17, - OP_CHANGE_BORDER_WIDTH = 18, - OP_CHANGE_CROSS_FADE = 19, - OP_CHANGE_UNBLURRED_OUTSET_SHADOW = 20, - OP_CLEAR = 21, - OP_DRAW = 22, - OP_DUMP_FRAMEBUFFER = 23, - OP_PUSH_DEBUG_GROUP = 24, - OP_POP_DEBUG_GROUP = 25, - OP_CHANGE_BLEND = 26, + OP_CHANGE_RADIAL_GRADIENT = 12, + OP_CHANGE_COLOR_MATRIX = 13, + OP_CHANGE_BLUR = 14, + OP_CHANGE_INSET_SHADOW = 15, + OP_CHANGE_OUTSET_SHADOW = 16, + OP_CHANGE_BORDER = 17, + OP_CHANGE_BORDER_COLOR = 18, + OP_CHANGE_BORDER_WIDTH = 19, + OP_CHANGE_CROSS_FADE = 20, + OP_CHANGE_UNBLURRED_OUTSET_SHADOW = 21, + OP_CLEAR = 22, + OP_DRAW = 23, + OP_DUMP_FRAMEBUFFER = 24, + OP_PUSH_DEBUG_GROUP = 25, + OP_POP_DEBUG_GROUP = 26, + OP_CHANGE_BLEND = 27, OP_LAST } OpKind; @@ -137,6 +138,16 @@ typedef struct float end_point[2]; } OpLinearGradient; +typedef struct +{ + ColorStopUniformValue color_stops; + IntUniformValue n_color_stops; + float start; + float end; + float radius[2]; + float center[2]; +} OpRadialGradient; + typedef struct { const graphene_matrix_t *matrix; diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index e383ef88be..efb49738b9 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -394,8 +394,6 @@ struct _GskRadialGradientNode graphene_point_t center; - gboolean circle; - float hradius; float vradius; float start; diff --git a/gsk/meson.build b/gsk/meson.build index 9cc22359cb..c3567d78ed 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -7,6 +7,7 @@ gsk_private_gl_shaders = [ 'resources/glsl/coloring.glsl', 'resources/glsl/color.glsl', 'resources/glsl/linear_gradient.glsl', + 'resources/glsl/radial_gradient.glsl', 'resources/glsl/color_matrix.glsl', 'resources/glsl/blur.glsl', 'resources/glsl/inset_shadow.glsl', diff --git a/gsk/resources/glsl/radial_gradient.glsl b/gsk/resources/glsl/radial_gradient.glsl new file mode 100644 index 0000000000..84673fc470 --- /dev/null +++ b/gsk/resources/glsl/radial_gradient.glsl @@ -0,0 +1,89 @@ +// VERTEX_SHADER +uniform float u_start; +uniform float u_end; +uniform float u_color_stops[8 * 5]; +uniform int u_num_color_stops; +uniform vec2 u_radius; +uniform vec2 u_center; + +_OUT_ vec2 center; +_OUT_ vec4 color_stops[8]; +_OUT_ float color_offsets[8]; +_OUT_ float start; +_OUT_ float end; + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + center = (u_modelview * vec4(u_center, 0, 1)).xy; + start = u_start; + end = u_end; + + for (int i = 0; i < u_num_color_stops; i ++) { + color_offsets[i] = u_color_stops[(i * 5) + 0]; + color_stops[i].r = u_color_stops[(i * 5) + 1]; + color_stops[i].g = u_color_stops[(i * 5) + 2]; + color_stops[i].b = u_color_stops[(i * 5) + 3]; + color_stops[i].a = u_color_stops[(i * 5) + 4]; + } +} + +// FRAGMENT_SHADER: +#ifdef GSK_LEGACY +uniform int u_num_color_stops; +#else +uniform highp int u_num_color_stops; +#endif + +uniform vec2 u_radius; +uniform float u_end; + +_IN_ vec2 center; +_IN_ vec4 color_stops[8]; +_IN_ float color_offsets[8]; +_IN_ float start; +_IN_ float end; + +// The offsets in the color stops are relative to the +// start and end values of the gradient. +float abs_offset(float offset) { + return start + ((end - start) * offset); +} + +vec4 premultiply(vec4 c) { + vec4 k = vec4(c.rgb * c.a, c.a); + return k; +} + +void main() { + vec2 pixel = get_frag_coord(); + vec2 rel = (center - pixel) / (u_radius); + float d = sqrt(dot(rel, rel)); + + if (d < abs_offset (color_offsets[0])) { + setOutputColor(premultiply(color_stops[0]) * u_alpha); + return; + } + + if (d > end) { + setOutputColor(premultiply(color_stops[u_num_color_stops - 1]) * u_alpha); + return; + } + + vec4 color = vec4(0, 0, 0, 0); + for (int i = 1; i < u_num_color_stops; i++) { + float last_offset = abs_offset(color_offsets[i - 1]); + float this_offset = abs_offset(color_offsets[i]); + + // We have color_stops[i - 1] at last_offset and color_stops[i] at this_offset. + // We now need to map `d` between those two offsets and simply mix linearly between them + if (d >= last_offset && d <= this_offset) { + float f = (d - last_offset) / (this_offset - last_offset); + + color = mix(color_stops[i - 1], color_stops[i], f); + break; + } + } + + setOutputColor(premultiply(color) * u_alpha); +} -- 2.30.2